package com.berchen.code.rsa;

import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.CharSet;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA加密
 */
public class RsaTest {

    /**
     * 校验数字签名
     * @param input     原文
     * @param algorithm 数字签名算法
     * @param pubKey       校验签名的公钥KEY
     * @param signatureDate 数字签名（被校验的对象）
     * @return
     * @throws Exception
     */
    private static boolean verifySignature(String input,String algorithm,PublicKey pubKey ,String signatureDate)throws Exception{

        // 获取签名对象
        Signature signature = Signature.getInstance(algorithm);
        // 初始化校验
        signature.initVerify(pubKey);
        // 传入原文
        signature.update(input.getBytes());

        // 校验数据
        return signature.verify(Base64.decode(signatureDate));
    }

    /**
     * 获取数字签名
     * @param input 原文
     * @param algorithm 数字签名算法
     * @param priKey    私钥KEY
     * @return
     * @throws Exception
     */
    private static String getSignature(String input,String algorithm,PrivateKey priKey)throws Exception{

        Signature signature = Signature.getInstance(algorithm);
        // 初始化校验
        signature.initSign(priKey);
        // 传入原文
        signature.update(input.getBytes());

        // 开始签名
        byte[] sign = signature.sign();
        return Base64.encode(sign);

    }
    /**
     * 生成密钥 并且将密钥字符串保存到文件
     *
     * @param algorithm
     * @param pubPath
     * @param priPath
     */
    private static void saveKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {

        File pubFile = new File(pubPath);
        File priFile = new File(priPath);

        if(!pubFile.exists()||!priFile.exists()) {
            // 创建密钥对
            // 密钥对生成器对象
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
            // 生成密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 生成公钥
            PublicKey aPublic = keyPair.getPublic();
            // 获取公钥字节数组
            byte[] pubByte = aPublic.getEncoded();
            String pubKey = Base64.encode(pubByte);

            FileUtils.writeStringToFile(pubFile,pubKey, Charset.forName("UTF-8"));

            // 生成私钥
            PrivateKey aPrivate = keyPair.getPrivate();
            // 获取私钥字节数组
            byte[] priByte = aPrivate.getEncoded();

            // 使用Base64进行转码
            String priKey = Base64.encode(priByte);
            FileUtils.writeStringToFile(priFile,priKey,Charset.forName("UTF-8"));
        }
    }

    /**
     * 读取私钥字符串 生成私钥KEY对象
     * @param algorithm
     * @param priPath
     * @return
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(String algorithm,String priPath)throws Exception{
        String priKeyStr = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
        // 创建生成KEY的工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 创建私钥KEY 的规则
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(priKeyStr));
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }

    /**
     * 获取公钥字符串 生成公钥KEY对象
     * @param algorithm
     * @param pubPath
     * @return
     * @throws Exception
     */
    private static PublicKey getPublicKey(String algorithm,String pubPath)throws Exception{
        String pubKeyStr = FileUtils.readFileToString(new File(pubPath), Charset.defaultCharset());
        // 创建生成KEY的工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 创建公钥KEY 的规则
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(pubKeyStr));
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

    /**
     * 加密方法
     * @param algorithm 算法
     * @param input     原文
     * @param key       加密的key  和解密的key必须是相对的  公钥加密必须私钥解密 私钥加密必须公钥解密
     * @return
     * @throws Exception
     */
    private static String encrypt(String algorithm,String input,Key key) throws Exception{
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);

        // 对加密进行初始化
        // 第一个个参数 加密模式
        // 第二个参数  你想使用公钥还是私钥加密
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 使用私钥进行加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        return Base64.encode(bytes);
    }

    /**
     * 解密方法
     * @param algorithm     算法
     * @param output        密文 Base64转码之后的
     * @param key           解密的密钥对象
     * @return
     * @throws Exception
     */
    private static String decrypt(String algorithm,String output,Key key) throws Exception{
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);

        // 解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(Base64.decode(output)));
    }
    public static void main(String[] args) throws Exception{

        String algorithm = "RSA";

        // 保存密钥到文件
        saveKeyToFile(algorithm,"pub.pub","pri.pri");

        // 加密
        String miwen = encrypt(algorithm, "陈琬皓", getPrivateKey(algorithm, "pri.pri"));
        System.out.println(miwen);

        // 解密
        String decrypt = decrypt(algorithm, miwen, getPublicKey(algorithm, "pub.pub"));
        System.out.println(decrypt);


        // 获取数字签名
        String signature = getSignature("陈琬皓", "sha256withrsa", getPrivateKey(algorithm, "pri.pri"));
        System.out.println(signature);
        // 验证数字签名
        boolean b = verifySignature("陈琬皓", "sha256withrsa", getPublicKey(algorithm, "pub.pub"), signature);
        System.out.println(b);

        /*// 算法
        String algorithm = "RSA";
        // 创建密钥对
        // 密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);

        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 生成公钥 私钥
        PublicKey aPublic = keyPair.getPublic();
        PrivateKey aPrivate = keyPair.getPrivate();

        // 获取私钥字节数组
        byte[] priByte = aPrivate.getEncoded();
        // 获取公钥字节数组
        byte[] pubByte = aPublic.getEncoded();


        // 使用Base64进行转码
        String priKey = Base64.encode(priByte);
        String pubKey = Base64.encode(pubByte);
//        System.out.println(priKey);
//        System.out.println(pubKey);

        // 保存公钥 私钥 到文件中


        // 原文
        String input = "陈琬皓";

        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);

        // 对加密进行初始化
        // 第一个个参数 加密模式
        // 第二个参数  你想使用公钥还是私钥加密
        cipher.init(Cipher.ENCRYPT_MODE, aPrivate);
        // 使用私钥进行加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        String miwen = Base64.encode(bytes);
        System.out.println(miwen);


        // 解密
        cipher.init(Cipher.DECRYPT_MODE, aPublic);
        byte[] bytes1 = cipher.doFinal(bytes);
        String yuanwen = new String(bytes1);
        System.out.println(yuanwen);*/
    }
}
